package com.evil.application.listener;

import com.alibaba.fastjson.JSON;
import com.evil.application.annotation.Automation;
import com.evil.application.mapper.ApplicationInfoMapper;
import com.evil.application.pojo.dto.mq.ExcelImportMQReqDTO;
import com.evil.application.pojo.dto.order.imp.ExcelImportOrderDTO;
import com.evil.application.pojo.dto.order.imp.ExcelImportResultDTO;
import com.evil.application.pojo.dto.queue.QueueContentDTO;
import com.evil.application.pojo.entity.*;
import com.evil.application.service.*;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.enums.queue.QueueStatusEnum;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.file.constant.OssConstant;
import com.evil.common.file.dto.FileRespDTO;
import com.evil.common.file.dto.UploadReqDTO;
import com.evil.common.file.enums.BusTypeEnum;
import com.evil.common.file.util.PoiUtil;
import com.evil.common.rocketmq.constant.MQConstant;
import com.evil.file.api.RemoteFileService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 导入应用订单 队列消费着
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@Component
@AllArgsConstructor
@RocketMQMessageListener(
        topic = MQConstant.IMPORT_APPLICATION_ORDER_MQ_KEY,
        consumerGroup = MQConstant.IMPORT_APPLICATION_ORDER_CONSUMER_GROUP
)
public class ImportAppOrderRocketMqConsumer implements RocketMQListener<ExcelImportMQReqDTO> {

    private final LoginUtil loginUtil;

    private final ApplicationInfoMapper applicationInfoMapper;

    private final ApplicationFormService applicationFormService;

    private final ApplicationOrderService applicationOrderService;

    private final ApplicationOrderDataService applicationOrderDataService;

    private final ApplicationOrderNodeHandlerService applicationOrderNodeHandlerService;

    private final AppOrderOperationService appOrderOperationService;

    private final ApplicationQueueService applicationQueueService;

    private final ElasticsearchService elasticsearchService;

    private final RemoteFileService remoteFileService;

    @Override
    @Automation
    public void onMessage(@Validated ExcelImportMQReqDTO excelImportMQReqDTO) {
        Long fileId = excelImportMQReqDTO.getFileId();
        String applicationKey = excelImportMQReqDTO.getApplicationKey();
        Long queueId = excelImportMQReqDTO.getQueueId();
        log.info("Receive import application order message：fileId:{}, applicationKey:{}, queueId:{}", fileId, applicationKey, queueId);

        FileRespDTO file = remoteFileService.findBaseByFileId(fileId);
        // 校验文件
        Workbook workbook = this.verifyFile(file);

        // 获取应用
        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        // 获取应用表单
        ApplicationForm applicationForm = applicationFormService.releaseForm(applicationInfo.getApplicationId());

        // 导入处理
        ExcelImportResultDTO excelImportResultDTO = appOrderOperationService.handleExcelImport(workbook, applicationForm);
        if (!excelImportResultDTO.getHasError()) {
            // 入库（若异常则回滚，队列重新消费）
            this.saveImport(excelImportResultDTO.getOrders(), applicationInfo, applicationForm, queueId);
        } else {
            // 错误处理
            this.handleImportError(workbook, file.getFileName(), queueId, excelImportResultDTO.getOtherErrorMsg());
        }
    }

    /**
     * 保存入库
     *
     * @param importOrders    importOrders
     * @param applicationForm applicationForm
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveImport(List<ExcelImportOrderDTO> importOrders, ApplicationInfo applicationInfo, ApplicationForm applicationForm, Long queueId) {
        // 处理订单
        Map<ApplicationOrder, ExcelImportOrderDTO> orderImportMap = StreamUtil.toOrderlyMapK(importOrders, o ->
                applicationOrderService.handleImportOrders(applicationInfo, applicationForm, o.getControlDataList()));
        // 保存订单
        applicationOrderService.saveOrders(orderImportMap.keySet());
        // 处理订单数据
        List<ApplicationOrderData> orderDataList = new ArrayList<>();
        orderImportMap.forEach((order, orderDTO) -> {
            order.setCreatorId(orderDTO.getCreatorUserId());
            orderDataList.addAll(applicationOrderDataService.handle(applicationInfo, applicationForm, order, orderDTO.getControlDataList(), true));
        });
        // 保存订单数据
        applicationOrderDataService.save(orderDataList);
        // 刷新ES订单
        List<Long> orderIds = StreamUtil.transListT(orderImportMap.keySet(), ApplicationOrder::getOrderId);
        applicationOrderDataService.refreshEsOrderIds(orderIds);
        // 更新应用队列结果
        QueueContentDTO queueContentDTO = new QueueContentDTO(null, String.format("%s 条数据导入成功", orderImportMap.size()));
        applicationQueueService.updateQueueResult(queueId, QueueStatusEnum.COMPLETE, JSON.toJSONString(queueContentDTO));
    }

    /**
     * 导入失败处理
     *
     * @param workbook      workbook
     * @param fileName      fileName
     * @param queueId       queueId
     * @param otherErrorMsg otherErrorMsg
     */
    private void handleImportError(Workbook workbook, String fileName, Long queueId, String otherErrorMsg) {
        MultipartFile file;
        try {
            file = PoiUtil.workbookToMultipartFile(workbook, fileName, false);
        } catch (Exception e) {
            return;
        }

        // 上传文件 新增file数据
        UploadReqDTO uploadReqDTO = new UploadReqDTO();
        uploadReqDTO.setEnterpriseId(loginUtil.getLoginEnterpriseId());
        uploadReqDTO.setFile(file);
        uploadReqDTO.setType(BusTypeEnum.APPLICATION_ORDER_IMPORT_ERROR_FILE.getId());
        uploadReqDTO.setUserId(loginUtil.getLoginUserId());
        uploadReqDTO.setAllowContentType(OssConstant.UPLOAD_EXCEL_TYPE);
        uploadReqDTO.setAllowMaxSize(OssConstant.UPLOAD_FILE_SIZE);
        FileRespDTO fileRespDTO = remoteFileService.uploadFile(uploadReqDTO);

        // 更新应用队列结果
        QueueContentDTO queueContentDTO = new QueueContentDTO(fileRespDTO.getFileCode(), otherErrorMsg);
        applicationQueueService.updateQueueResult(queueId, QueueStatusEnum.FAILED, JSON.toJSONString(queueContentDTO));
    }

    /**
     * 校验导入文件
     *
     * @param file file
     */
    private Workbook verifyFile(FileRespDTO file) {
        Workbook workbook = null;
        try {
            // 接收文件byte数据
            byte[] fileBytes = remoteFileService.getFileByteByFileBase(file);

            // 根据不同类型文件创建不同的workbook
            if (PoiUtil.isExcel03(file.getFileName())) {
                workbook = new HSSFWorkbook(new ByteArrayInputStream(fileBytes));
            } else if (PoiUtil.isExcel07(file.getFileName())) {
                workbook = new XSSFWorkbook(new ByteArrayInputStream(fileBytes));
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, e.getMessage());
        }

        return workbook;
    }
}
